open Utils.Pervasive
open Utils.ListSpecial
open Utils.RandSelect
open Utils.Statistics
open Hashtbl
open Syntax
open Binding
open Core
open PrintExpr
open Pointers 
	
open BlockPopulation.Gene
open BlockPopulation.GenePool


open ProofAssembler.ProoProg
open ProofAssembler
open Rules
open TestCases
open List

(*Private utils******************************)
let mprf str = prf str; read_line( )

module Genotypes = struct
	type genoStatus = Dead of float | Alive of float | Unborn
	type genotype = program
	type genotype_evaluation = NoEval 
		| EvalFunc of float * (term -> float)
		| TestCases of test_case list
	let rec string_of_genotype ?(separator="\n") genes = function 
		| In id -> 
			string_of_int id
		| TypeFunc (prog, ty)-> 
			(string_of_genotype ~separator genes prog) ^ 
			"::" ^ (string_of_type !(genes.ctx) ty Compact)
		| ObjFunc (prog1, prog2) -> 
			(string_of_genotype ~separator genes prog1) ^  
			" ][ " ^ (string_of_genotype ~separator genes prog2)
end	


open ProofAssembler.Proof	
module Species = struct 
	type species_info = {
		species_id : int;
		ancestor_species_id : int option;
		mutable fillProportion : float;
		mutable theoreticalMaxFill : int;
		mutable currentFill : int;
		mutable avgComplexity : float;
		mutable avgFitness : float;
	}	
	let emptyInfo id a_id = {
		species_id = id;
		ancestor_species_id = a_id;
		fillProportion = 0.;
		theoreticalMaxFill = 0;
		currentFill = 0;
		avgComplexity = 0.;
		avgFitness =0.;
	}	
	type population = {
		pattern : proof;
		genotypes : (Genotypes.genotype, Genotypes.genoStatus) t ;
		mutable info : species_info;
		mutable contains_solution : bool
	}
	let kill_g population genotype = 
		match Hashtbl.find population.genotypes genotype with
		|Genotypes.Alive ev -> Hashtbl.replace population.genotypes genotype (Genotypes.Dead ev)
		|_->()
		 
	
	let empty_population id a_id pr = {
		pattern = pr;
		genotypes = create 200;
		info = emptyInfo id a_id;
		contains_solution =false
	}
	let card_population ?(pred=(fun _-> true))  population = 
		Hashtbl.fold (fun  genotype status acc ->
			if pred status then acc + 1 else acc) population.genotypes 0
	let iter_population ?(pred=(fun _-> true)) ?(f=(fun _ _ -> ( )))   population = 
	Hashtbl.iter
	(fun genotype status -> if pred status then f genotype status)
	population
	
	let get_population_segment pred population =
		Hashtbl.fold 
		(fun  genotype status lst ->
			if pred status then genotype :: lst
			else lst) population.genotypes []
	
	let get_live population = 
		Hashtbl.fold 
		(fun  genotype status lst ->
			match status with
			|Genotypes.Alive ev -> (ev , genotype) :: lst
			|_->lst) population.genotypes []
end

open Genotypes
open Species
module GP = struct
	type ecoSetting = {
		maxProofWidth : int;
		aprNumberOfLiveGenotypes : int;
		aprGenePoolTotComp : int;
		(*Sel_1*) elitismSize : int;
		selectSize : int;
		selectReap : int
	}
	type ecosystem  = {
		mutable solutions : term list;
		mutable species_count : int;
		mutable generation_count : int;
		mutable stats : string;
		mutable genotype_evaluator : Genotypes.genotype_evaluation;
		fossil_record : (proof , int option) t;
		settings : ecoSetting;
		goal_type : ty; 
		genes : gene_pool ref; 
		kingdom : (proof,  Species.population) t  
	}	
	let kill eco species genotype = 
		kill_g (Hashtbl.find eco.kingdom species) genotype
	let push_stat eco stat = eco.stats <-   eco.stats ^ ("\n" ^ stat) 
	let card_ecosystem ?(pred=(fun _->true)) eco = 
		Hashtbl.fold (fun _ population acc -> acc + (Species.card_population ~pred population))  eco.kingdom 0 		
	
	

	(*Species related*****************************)	
	let new_species_id eco = 
		let id = eco.species_count in
		eco.species_count <- eco.species_count + 1;
		id		
	let get_species_id eco species = 
		let ctx = !(!(eco.genes).ctx) in
		let idOpt = 
			Hashtbl.fold (fun spec pop acc -> 
				if (ProofAssembler.Proof.is_equiv ctx spec species) 
				then Some (pop.info.species_id , pop.info.ancestor_species_id)
				else acc) eco.kingdom None in 
		match idOpt with 
			|None -> raise Not_found
			|Some id -> id			
	let get_species_from_id eco id = 
		let speciesOpt = 
		Hashtbl.fold 
		(fun spec pop acc -> 
				if (pop.info.species_id = id) 
					then Some spec 
					else acc) eco.kingdom None
		in match speciesOpt with 
			| None -> raise Not_found
			|Some species -> species					
	
			
	let update_genes eco = 
		let genes = !(eco.genes) in
		reset_values genes;
		let rec update ev = 
			function 
			| ProgNode ty, In id -> 
				let catInfo, lst = tyFind genes ty in
				catInfo.species_use <- catInfo.species_use + 1;
				let gene = List.find (fun g -> g.gene_id = id) lst in
				gene.useInPopulation <- gene.useInPopulation +1;
				gene.geneFitness <- gene.geneFitness  +. ev;
				if gene.scoreOfBestCarrier < ev then gene.scoreOfBestCarrier <- ev
			| ForAllElim (proof, ty1), TypeFunc (prog, ty2) -> 
				update ev (proof,prog) 
			| (ImplyElim (proof1, proof2)) , (ObjFunc (prog1, prog2)) ->
				update ev (proof1, prog1); update ev (proof2, prog2) 
			| _->failwith "Invalid pair proof/program" in
		Hashtbl.iter (fun species population ->
			Hashtbl.iter (fun prog -> function 
			| Alive ev -> update ev (species, prog);
			|_->( )) population.genotypes) eco.kingdom;
		
		let apply_to_genes app = 
			Hashtbl.iter 
			(fun ty (cinfo, geneLst) -> if cinfo.species_use > 0 then (List.iter app geneLst)) genes.g in
		let best_fit_sum = ref 0.0 in		
		apply_to_genes 
		(fun gene -> 
			match gene.useInPopulation with 
			| x when x > 0 ->
				gene.geneFitness <- gene.geneFitness /. float gene.useInPopulation;
				gene.selectionProb <- gene.geneFitness +. gene.scoreOfBestCarrier; 
				if  gene.selectionProb > !best_fit_sum then best_fit_sum := gene.selectionProb;
			|_->( ));
		apply_to_genes 
		(fun gene -> gene.selectionProb <- gene.selectionProb /. !best_fit_sum)
	let update_species eco species = 
		let genes = !(eco.genes) in
		let population = Hashtbl.find eco.kingdom species in 
		let genotypes = population.genotypes in
		let genos = progs_of_proof genes species in 
		
		List.iter (fun genotype -> 
				if not (Hashtbl.mem genotypes genotype) then (
				Hashtbl.add genotypes genotype Genotypes.Unborn)) genos;	
		Hashtbl.iter (fun  g _ -> 
			if not (List.mem g	genos) then  Hashtbl.remove genotypes g) genotypes ;
		
		let ctr = ref 0 in
		let count = ref 0 in
		let sum = ref 0. in
		let best = ref 0. in
		Hashtbl.iter (fun genotype -> 
				function  	
					| Alive ev -> 
							count := !count + 1;
							sum := !sum +. ev;
							if ev > !best then best := ev
					| _ -> ( )) genotypes; 
		let liveGenos = get_live population in
		let num_genotypes_alive = List.length liveGenos in
		let (id,aid) = get_species_id eco species in 
		let new_info = {
					species_id = id;
					ancestor_species_id = aid;
					theoreticalMaxFill = List.length genos;
					currentFill = num_genotypes_alive;
					fillProportion = if List.length genos > 0 
						then float num_genotypes_alive /. float (List.length genos)
						else 0.0;
					avgComplexity =  0.0;
					avgFitness = if num_genotypes_alive > 0 then mean liveGenos (fun (ev,_)->ev) else 0.} in 	
		population.info <- new_info
	let update_eco eco = 
		 update_genes eco;	 
		 Hashtbl.iter (fun species pop -> update_species eco species) eco.kingdom		
	let rec update_population ?(debug=false) eco proof a_id =
		let deb ?(break=false) str = if debug then (if break then un(mprf str) else prf str) in
		let g_pop = !(eco.genes) in
		if Hashtbl.mem eco.kingdom proof 
			then (if is_complete proof then (
				deb "\nSpecies add start";
				let population = 
					try Hashtbl.find eco.kingdom proof 
					with Not_found -> assert(0=1); failwith "Shouldn't happen" in
				(try update_species eco proof with _ -> failwith "Unable to construct population")))
			else (
				Hashtbl.add eco.kingdom proof {
					pattern = proof;
					genotypes = Hashtbl.create 100;
					info = emptyInfo (new_species_id eco) a_id;
					contains_solution = false};
				update_population eco proof a_id)		
	let add_species ?(debug=false) eco proof a_id = 
		let ctx = !(!(eco.genes).ctx) in
		assert (ty_eqv ctx (check_tree ctx proof) eco.goal_type);
		update_population ~debug eco proof a_id;
		get_species_id eco proof



	
	(* Species mutation and creation******************)	
	let implyTypes goal_type genes = 
		let candidateTypes genes gt = 
			get_types genes ~pred:(fun ty ->type_to !(genes.ctx) gt ty) in
		
		ListLabels.map (candidateTypes genes goal_type)
		~f:(function 
			|ty when ty_eqv !(genes.ctx) ty goal_type -> ProgNode ty
			|ty -> Partial (goal_type, Some (ProgNode ty))
		(*ImplyElim (ProgNode ty, Partial (ty1, None))*)
			|_->  assert (1=0);failwith "Not supported")					
	let rec mp proof eco depth goalType = 
		assert (depth >=1);
		let genes = !(eco.genes) in
		let ctx = !(genes.ctx) in
		let proofType = check_tree ctx proof in
		if ty_eqv ctx proofType goalType 
		then(
			match depth with 
			|_ when depth = 1 -> 
				(match proof with 
					| Partial (tyGoal, Some proof) -> 
						ListLabels.map (mp proof eco 1 tyGoal)
						~f:(fun proof -> 
						if ty_eqv ctx tyGoal (check_tree ctx proof) 	
						then proof 
						else Partial(tyGoal, Some proof))
					| Partial (ty, None) -> implyTypes ty genes 
					| _->[proof])	
			|_-> match proof with
				| ProgNode ty -> [ProgNode ty] 
				| Partial (tyGoal, _) -> mp proof eco 1 tyGoal
				| ImplyElim(proof1, proof2)->
					cartesianProduct 
					(fun proof1 proof2 -> ImplyElim(proof1,proof2))
					(mp proof1 eco (depth - 1) (check_tree ctx proof1)) 
					(mp proof2 eco (depth - 1) (check_tree ctx proof2)) 
				|_->  [ ])
		else(
		match proofType with 
		|TyArr(ty1, ty2) -> [ImplyElim (proof, (Partial (ty1,None)))]
		| _ -> assert (1=0);failwith "???")		
	let modifyProofs ?(proofs=[]) eco depth = 
		assert (depth >= 0);
		let genes = !(eco.genes) in
		match depth with
			|x when x = 0 -> 
				ListLabels.map (implyTypes eco.goal_type genes)
				~f:(fun proof -> (None,proof))	
			|_ -> 
		let proofs = match proofs with
			|[ ] -> keys eco.kingdom
			|_->proofs in
		List.flatten (
			ListLabels.map proofs
			~f:(fun proof -> 
				let aid = fst (get_species_id eco proof) in
				ListLabels.map (mp proof eco depth eco.goal_type)
				~f:(fun p -> (Some aid, p))))		
	
	(* Evaluation *********************************)
	let prog_to_spawn eco = 
			eco.settings.aprNumberOfLiveGenotypes - 
			(card_ecosystem ~pred:(function Alive _->true |_->false) eco)
	
	let evaluate_program eco species prog = 
			let genes = !(eco.genes) in
			let ctx = !(genes.ctx) in
			let te = term_of_program genes species prog in 
			let (score,max) = 
				match eco.genotype_evaluator with
				| NoEval -> 
					assert(0=1); failwith "No fitness function defined" 
				| EvalFunc (m, fit_func) -> 
					(fit_func te, m)
				| TestCases tests ->
					(ListLabels.fold_left ~init:0.0 tests
					~f:(fun acc test -> if match_case te ctx test then acc +. 1.0 else acc)),
					(float (List.length tests)) in
			if score >= max then (
				prf ("\nFound solution :" ^ string_of_term ctx te Compact);
				(Hashtbl.find eco.kingdom  species).contains_solution <- true;
				if not (List.exists (fun lstTe ->  te_eqv ctx lstTe te) eco.solutions) then  
				eco.solutions <- te :: eco.solutions);
			prf ("\n Evaluating genotype " 
							^ string_of_int (card_ecosystem ~pred:(function Alive _->true |_->false) eco)
							^ " of (apr) " 
							^ string_of_int eco.settings.aprNumberOfLiveGenotypes);
			Hashtbl.replace (Hashtbl.find eco.kingdom species).genotypes prog (Alive score); 
			score
	let evaluate_ecosystem eco = 

		let evaluate_genotypes eco = 
			assert(card_ecosystem eco > 0);		

			let prob = (float eco.settings.aprNumberOfLiveGenotypes) /. float (card_ecosystem eco) in
			let ctr = ref 0 in
			let count = ref 0 in
			let sum = ref 0. in
			let best = ref 0. in
			Hashtbl.iter (fun species population ->
				Hashtbl.iter (fun prog -> 
					let num_needed = prog_to_spawn eco in
					let num_unborn = card_ecosystem ~pred:(function Unborn -> true |_->false) eco in
					
				function  
					|Unborn -> 
						if Random.float 1.0 <= prob && !ctr < eco.settings.aprNumberOfLiveGenotypes then (	
							ctr := !ctr + 1;
							let eval = evaluate_program eco species prog  in
							count := !count + 1;
							sum := !sum +. eval;
							if eval > !best then best := eval)			
					| Alive ev -> 
							count := !count + 1;
							sum := !sum +. ev;
							if ev > !best then best := ev
					| _ -> ( )) population.genotypes) eco.kingdom; 
			prf ("\nAverage genotype fitness = " ^ (string_of_float (!sum /. float !count)));
			prf ("\nBest genotype score = " ^ (string_of_float !best)) in
		let evaluate_species eco = 
			let es species population = 
			population.info.avgFitness <- 0.0;
		let num = ref 0.0 in
		let fit = ref 0.0 in
		Hashtbl.iter (fun _ status -> 
			match status with 
			| Alive ev -> 
				num := !num +. 1.0;
				fit := !fit +. ev
			|_->( )) population.genotypes;
		if !num > 0.0 then population.info.avgFitness <- !fit /. !num in
		Hashtbl.iter es eco.kingdom in
		evaluate_genotypes eco;
		evaluate_species eco;
		update_genes eco
	let ghost_ecosystem genes = {
		settings = {
			maxProofWidth = 10;
			aprNumberOfLiveGenotypes = 0;
			aprGenePoolTotComp = 0;
			elitismSize = 0;
			selectSize = 0;
			selectReap = 0 
		};
		stats ="";
		solutions = [ ];
		species_count = 0;
		generation_count = 0 ;
		goal_type = TyArr(TyUnit, TyUnit);
		genotype_evaluator = Genotypes.NoEval ;
		genes = genes;
		fossil_record = create 0;
		kingdom = create 0
	}
	let make_ecosystem ?(debug=false) genes_ref goal ge settings  = {
		solutions = [ ];
		stats = "";
		species_count = 0;
		generation_count = 0;
		settings = settings;
		goal_type = goal;
		genotype_evaluator = ge;
		genes = genes_ref;
		kingdom = create 300;
		fossil_record = create 50
	} 
	let update_species_geneType_use ?(remove_unused=false) eco =
		let genes = !(eco.genes) in 
		reset_values genes;
		let rec update species = 
			match species  with 
			| ProgNode ty  -> 
				let catInfo, _ = tyFind genes ty in
				catInfo.species_use <- catInfo.species_use + 1
			| ForAllElim (proof, ty1)  -> 
				update proof
			| ImplyElim (proof1, proof2) -> 
				update proof1; update proof2
			| _->failwith "Invalid pair proof/program" in
		Hashtbl.iter (fun species _ -> update species) eco.kingdom;
		if remove_unused then (
			Hashtbl.iter 
			(fun ty (cinfo, geneLst) -> 	
			if cinfo.species_use = 0 then rem_type genes ty) 
			genes.g)	
	let speciesAccept ?(max=20) eco new_proofs =
		let genes  = !(eco.genes) in 
		let add proofLst = 
			ListLabels.iter proofLst
			~f:(fun (ancesterID, proof) -> 
			if not (ProofAssembler.exists !(genes.ctx) (keys eco.kingdom) proof) 
			then (un(add_species eco proof ancesterID))) in	
		prf ("\n"^string_of_int (List.length new_proofs));
		let (new_complete_proofs, new_incomplete_proofs) = 
			ListLabels.partition new_proofs 
			~f:(fun (_,pr) -> 
			is_complete pr && 
			(not (ProofAssembler.exists !(genes.ctx) (keys eco.kingdom) pr))) in
		prf ("\nAdding "^string_of_int (List.length new_complete_proofs) ^ " complete proofs");
		add new_complete_proofs;
		let lst =  
			if List.length new_incomplete_proofs < max 
			then new_incomplete_proofs
			else choose_random ~selectionSize:max new_incomplete_proofs in
		prf ("\nAdding "^string_of_int (List.length lst)^ " incomplete proof");
		add lst		 		
	
	
	(*Selection *******************************************)
	let select_on_genes eco =
	(*Selection operation on genes based on their fitness function*) 
		let genes = !(eco.genes) in
		
		Hashtbl.iter (fun  ty (_ , geneList)  -> 
			rem_type genes ty;
			ListLabels.iter geneList
			~f:(fun gene ->
			if gene.selectionProb >= Random.float 1.0 then 
			insert_expression genes (Te(gene.expression,None)))) genes.g
	let makeProofs goOn eco  = 
		let depth = ref 0 in
		while goOn eco !depth do
			speciesAccept eco (modifyProofs eco !depth);
			depth := !depth + 1
		done	
	
end 

open GP
module GPRun = struct		
	let modifyProofs eco  = 
			try
			speciesAccept eco (modifyProofs eco 6)
			with _-> () 
	let fill_gene_pool ?(debug=false) eco  constraintFunc =
		let genes = !(eco.genes) in 
		let compGenes = comp genes in
		let compToMakeUp = eco.settings.aprGenePoolTotComp - compGenes in
		if compToMakeUp > 0 then( (*Rebuild BlockPopulation *)
				growToComplexity ~debug 
					~growUnit:(Complexity compToMakeUp)
					genes constraintFunc
					~onSuccess:(fun bpset _ -> prf "\nsuccess")
					~onFailure:(fun str -> prf "\nfailure")) 		
	let grim_reaper eco = 
		let rec reap eco = function 
			| x when x > 0 ->
				let species = hd (choose_random ~selectionSize:1 (keys eco.kingdom)) in
				let live_population = get_live (Hashtbl.find eco.kingdom species) in
				(match List.length live_population with
					| x when x > 0 ->
						prf ("\nreaping " ^ string_of_int x);
						let (ev, genotype) = 
							hd (rouletteWheelSelect live_population (fun (a,_) -> if a > 0. then 1. /. a else 0.)) in 
							kill eco species genotype;
							reap eco (x - 1)
					| _-> reap eco x)
			| _ ->( ) in
			
		let num_alive =  card_ecosystem ~pred:(function Alive _->true|_->false) eco in
		(match eco.settings.selectReap with 
		| x when x <= eco.settings.aprNumberOfLiveGenotypes - num_alive -> ()
		| _-> reap eco (eco.settings.selectReap - eco.settings.aprNumberOfLiveGenotypes - num_alive));
		update_genes eco;
		let rec inner acc = function
		| g :: rest -> 
			if g.useInPopulation = 0 && g.protection <> NoDelete 
				then inner acc rest else inner (g::acc) rest
		| [] -> acc in
		
		let genes = !(eco.genes) in
		Hashtbl.iter (fun ty (catInfo, geneLst) -> 
			replace_type genes ty (Some catInfo) (Some (inner [] geneLst))) genes.g
	
	let elitism eco = 
		let genes = !(eco.genes) in
		let arr = Array.make (eco.settings.elitismSize) (0., TmUnit) in 
		let sm_eq = (fun (a,_) (b,_) -> a <= b) in
		Hashtbl.iter (fun proof pop ->
		iter_population  pop.genotypes ~pred:(function Alive _ ->true |_ ->false) 
		~f:(fun prog -> 
				function 
				| Alive  ev -> 
						if sm_eq (Array.get arr 0) (ev, TmUnit) then(
							 Array.set arr 0 (ev , term_of_program genes proof prog);
							Sort.array sm_eq arr)
				|_ ->failwith "Genotype is dead")) eco.kingdom;
		
		Array.iter (fun (ev , ter) ->
			insert_expression ~ds:NoDelete genes (Te (ter, None));  
			prf ("\n===>" ^ string_of_float ev ^ " " ^ string_of_term !(genes.ctx) ter Alpha)) arr 
	let respawn_genotypes eco = 
		let num_to_spawn = 
			ref (eco.settings.aprNumberOfLiveGenotypes - 
			card_ecosystem ~pred:(function Alive _  -> true | _ -> false) eco) in
		while  !num_to_spawn > 0 do 
			let species = hd (choose_random ~selectionSize:1 (keys eco.kingdom)) in
			let population = Hashtbl.find eco.kingdom species in
			let unborn_genotypes = get_population_segment (function Unborn -> true |_->false) population in
			match List.length unborn_genotypes with
			| x when x > 0 ->
				let unborn_genotype = 
				hd (choose_random 
				~pred:(fun genotype -> match Hashtbl.find population.genotypes genotype with
				| Unborn -> true |_-> false) ~selectionSize:1 unborn_genotypes) in 
				evaluate_program eco species unborn_genotype ;
				num_to_spawn := !num_to_spawn - 1
			| _-> ( )
		done
		
	let main ?(debug=false)  eco constraintFunc = 
		elitism eco;
		Hashtbl.iter (fun proof pop ->
		iter_population  pop.genotypes ~pred:(function Alive _ ->true |_ ->false) 
		~f:(fun prog -> 
				function 
				| Alive  0.  -> Hashtbl.replace pop.genotypes prog (Dead 0.)
				|_ ->( ))) eco.kingdom;
		grim_reaper eco;
		fill_gene_pool ~debug eco (fun _ expr-> complexity_of_expr expr < 500);
		update_eco eco;
		respawn_genotypes eco;
		update_genes eco;
		eco.generation_count <- eco.generation_count + 1
	
		
	let rec next_generation ?(debug=false) eco constraintFunc  = 
		push_stat eco ("----\nGeneration " ^ (string_of_int eco.generation_count));
		let genes = !(eco.genes) in
		match eco.generation_count with 
		| x when x > 0 ->
			main eco constraintFunc
		|_-> 
			let rec fillPopulation maxTry currentTry = 
				let unborn_num = card_ecosystem ~pred:(function Unborn -> true |_->false) eco in
				let prog_to_spawn = prog_to_spawn eco in
				if  unborn_num >=  prog_to_spawn then ( )
			else (if currentTry >=  maxTry 
					then ( 
			prf ("\nThe system has tried to produce genotypes for " ^ (string_of_int maxTry) ^ " iterations");
			prf ("\nThere are " ^ (string_of_int unborn_num) ^ " possible genotypes in the gene pool");
			prf (" out of a still needed " ^ (string_of_int prog_to_spawn)); 
			prf "\nDo you wish to keep on trying(y/n)?";
			if (read_line( )) = "y" 
				then fillPopulation maxTry 0
				else ())
					else ( 
				fill_gene_pool ~debug eco  constraintFunc ;
				let goOn = (fun eco depth -> 
					prf ("\nProcess next (depth =" ^ string_of_int depth ^ ") ? ");
					if depth >= 6 then false else true) in
				makeProofs goOn eco;
				ListLabels.iter (keys eco.kingdom)
				~f: (fun species -> 
					if not (is_complete(species)) 
					then Hashtbl.remove eco.kingdom species);
				List.iter (fun species -> update_species eco species) (keys eco.kingdom);
				update_species_geneType_use ~remove_unused:true eco;
				fillPopulation maxTry (currentTry + 1))) in			
			fillPopulation 20 0;
			prf ("\n\tThere are "^ (string_of_int (List.length (keys eco.kingdom))) ^ " species");
			prf ("\n\t"^ string_of_int (card_ecosystem eco) ^ " possible genotypes");
			evaluate_ecosystem eco;
			eco.generation_count <- eco.generation_count + 1
	
end
